#include <dynamic_library.h>
#include <dlfcn.h>
#include <system_log.h>

namespace FF::Platform {

bool DynamicLibrary::Load(char const *path, LibraryLoadPolicy policy) noexcept
{
    if (handle_) {
        return true;
    }

    int flags = 0;

    switch (policy) {
        case LibraryLoadPolicy::GLOBAL:
            flags = static_cast<int>(RTLD_GLOBAL | RTLD_NOW);
            break;
        case LibraryLoadPolicy::LAZY:
            flags = RTLD_LAZY;
            break;
        case LibraryLoadPolicy::LOCAL:
            flags = static_cast<int>(RTLD_LOCAL | RTLD_NOW);
            break;
        default:
            // IMPOSSIBLE
            return false;
    }

    void *handle = dlopen(path, flags);
    if (handle) {
        handle_ = handle;
        name_ = path;
        return true;
    }

    FFLOGE("Platform::DynamicLibrary::Load - Can't load %s library, dlerror: %s", path, dlerror());
    return false;
}

bool DynamicLibrary::IsLoaded() const noexcept
{
    return handle_ != nullptr;
}

bool DynamicLibrary::Unload() noexcept
{
    if (!handle_) {
        return true;
    }

    if (!dlclose(handle_)) {
        handle_ = nullptr;
        return true;
    }

    FFLOGE("Platform::DynamicLibrary::Unload - Can't unload %s library, dlerror: %s", name_.c_str(),
        dlerror());

    return false;
}

void *DynamicLibrary::GetFunction(char const *name) const noexcept
{
    if (!IsLoaded()) {
        constexpr static char const msg[] =
            "Platform::DynamicLibrary::GetFunction - Can't find function %s because %s dynamic library "
            "is not loaded yet.";
        FFLOGE(msg, name, name_.c_str());

        return nullptr;
    }

    void *result = dlsym(handle_, name);

    if (!result) {
        FFLOGE("Platform::DynamicLibrary::GetFunction - Can't find %s function in the %s library, dlerror: %s",
            name, name_.c_str(), dlerror());
    }

    return result;
}

} // namespace FF::Platform
